Expand description
Provides a mechanism to lay out data into GPU buffers ensuring WGSL’s memory layout requirements are met.
§Features
- supports all WGSL host-shareable types + wrapper types (
&T
,&mut T
,Box<T>
, …) - supports data types from a multitude of crates as features
- covers a wide area of use cases (see examples)
§Motivation
Having to manually lay out data into GPU buffers can become very tedious and error prone. How do you make sure the data in the buffer is laid out correctly? Enforce it so that future changes don’t break this delicate balance?
encase
gives you the ability to make sure at compile time that your types will be laid out correctly.
§Design
The core trait is ShaderType
which mainly contains metadata about the given type.
The WriteInto
, ReadFrom
and CreateFrom
traits represent the ability of a type to be written into the buffer, read from the buffer and created from the buffer respectively.
Most data types can implement the above traits via their respective macros:
impl_vector!
for vectorsimpl_matrix!
for matricesimpl_rts_array!
for runtime-sized arraysimpl_wrapper!
for wrappersShaderType
for structs
The UniformBuffer
, StorageBuffer
, DynamicUniformBuffer
and DynamicStorageBuffer
structs are wrappers around an underlying raw buffer (a type implementing BufferRef
and/or BufferMut
depending on required capability). They facilitate the read/write/create operations.
§Examples
Write affine transform to uniform buffer
use encase::{ShaderType, UniformBuffer};
#[derive(ShaderType)]
struct AffineTransform2D {
matrix: glam::Mat2,
translate: glam::Vec2
}
let transform = AffineTransform2D {
matrix: glam::Mat2::IDENTITY,
translate: glam::Vec2::ZERO,
};
let mut buffer = UniformBuffer::new(Vec::<u8>::new());
buffer.write(&transform).unwrap();
let byte_buffer = buffer.into_inner();
// write byte_buffer to GPU
assert_eq!(&byte_buffer, &[0, 0, 128, 63, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 0, 0]);
Create vector instance by reading from dynamic uniform buffer at specific offset
use encase::DynamicUniformBuffer;
// read byte_buffer from GPU
let byte_buffer = [1u8; 256 + 8];
let mut buffer = DynamicUniformBuffer::new(&byte_buffer);
buffer.set_offset(256);
let vector: mint::Vector2<i32> = buffer.create().unwrap();
assert_eq!(vector, mint::Vector2 { x: 16843009, y: 16843009 });
Write and read back data from storage buffer
use encase::{ShaderType, ArrayLength, StorageBuffer};
#[derive(ShaderType)]
struct Positions {
length: ArrayLength,
#[size(runtime)]
positions: Vec<mint::Point2<f32>>
}
let mut positions = Positions {
length: ArrayLength,
positions: Vec::from([
mint::Point2 { x: 4.5, y: 3.4 },
mint::Point2 { x: 1.5, y: 7.4 },
mint::Point2 { x: 4.3, y: 1.9 },
])
};
let mut byte_buffer: Vec<u8> = Vec::new();
let mut buffer = StorageBuffer::new(&mut byte_buffer);
buffer.write(&positions).unwrap();
// write byte_buffer to GPU
// change length on GPU side
byte_buffer[0] = 2;
// read byte_buffer from GPU
let mut buffer = StorageBuffer::new(&mut byte_buffer);
buffer.read(&mut positions).unwrap();
assert_eq!(positions.positions.len(), 2);
Write different data types to dynamic storage buffer
use encase::{ShaderType, DynamicStorageBuffer};
let mut byte_buffer: Vec<u8> = Vec::new();
let mut buffer = DynamicStorageBuffer::new_with_alignment(&mut byte_buffer, 64);
let offsets = [
buffer.write(&[5.; 10]).unwrap(),
buffer.write(&vec![3u32; 20]).unwrap(),
buffer.write(&glam::Vec3::ONE).unwrap(),
];
// write byte_buffer to GPU
assert_eq!(offsets, [0, 64, 192]);
Supports writing to uninitialized memory as well.
use std::mem::MaybeUninit;
use encase::{ShaderType, DynamicStorageBuffer};
let mut uninit_buffer: Vec<MaybeUninit<u8>> = Vec::new();
let mut buffer = DynamicStorageBuffer::new_with_alignment(&mut uninit_buffer, 64);
let offsets = [
buffer.write(&[5.; 10]).unwrap(),
buffer.write(&vec![3u32; 20]).unwrap(),
buffer.write(&glam::Vec3::ONE).unwrap(),
];
// SAFETY: Vec<u8> and Vec<MaybeUninit<u8>> share the same layout.
let byte_buffer: Vec<u8> = unsafe {
Vec::from_raw_parts(
uninit_buffer.as_mut_ptr().cast(),
uninit_buffer.len(),
uninit_buffer.capacity()
)
};
std::mem::forget(uninit_buffer);
// write byte_buffer to GPU
assert_eq!(offsets, [0, 64, 192]);
Modules§
- Module containing items necessary to implement
ShaderType
for matrices - Module containing items necessary to implement
ShaderType
for runtime-sized arrays - Module containing items necessary to implement
ShaderType
for vectors
Macros§
- Used to implement
ShaderType
for the given matrix type - Used to implement
ShaderType
for the given runtime-sized array type - Used to implement
ShaderType
for the given vector type - Used to implement
ShaderType
for the given wrapper type
Structs§
- Helper type meant to be used together with the
ShaderType
derive macro - Dynamic storage buffer wrapper facilitating RW operations
- Dynamic uniform buffer wrapper facilitating RW operations
- Storage buffer wrapper facilitating RW operations
- Uniform buffer wrapper facilitating RW operations
Traits§
- Trait implemented for WGSL runtime-sized arrays and WGSL structs containing runtime-sized arrays (non fixed-footprint types)
- Trait implemented for all WGSL fixed-footprint types
- Base trait for all WGSL host-shareable types
Derive Macros§
- Used to implement
ShaderType
for structs